#include "listaadx.h"
#include <iostream>
#include <list>
#include "adx.h"
#include <iomanip>
#include <cstdlib>
#include <cmath>
#include <string>
#include <stdio.h>
#include <fstream>

using namespace std;
using std::setiosflags;
using std::ios;
using std::ofstream;
using std::cout;
using std::endl;
using std::setw;


/// construtor padrao
ListaADX::ListaADX()
{
}

/// metodo para calcular adx e gerar uma lista
/// @param periodo periodo a ser calculado no calculo ADX
/// @param lista dados de entrada do banco de dados
list<ADX*> ListaADX:: calculaADX( int periodo , list<ADX*> lista )
{

    if (lista.empty())
    {
        cerr << "\n Base de dados vazia ou incorreta.";
        return dadosGer;
    }


    //processa a selecao de dados externos
    if (!lista.empty() )
    {
        temp = new ADX(); //anteriores
        atual = new ADX(); // atual
        prox = new ADX(); // proximos

        iter1 = lista.begin();
        iter2 = lista.begin();
        iter2++;


        while ( iter1 != lista.end() )
        {
            atual = *iter1;
            prox = *iter2;

            // na ultima linha nada eh calculado
            if ( atual == lista.back())
            {
                temp->data = atual->data;
                temp->altaAtual = 0;
                temp->baixaAtual = 0;
                temp->dAlta = 0;
                temp->dBaixa = 0;
                temp->FV = 0;
                temp->FVMed = 0;
                temp->IDneg = 0;
                temp->IDpos = 0;
                temp->indMovDirNeg = 0;
                temp->indMovDirPos = 0;
                temp->MDMneg = 0;
                temp->MDMpos = 0;
                temp->resADX = 0;
            }
            else
            {
                //a lista eh calculada na mesma ordem de leitura do arquivo = data +atual para data+antiga
                temp->deltaAlta(atual->altaAtual, prox->altaAtual);
                temp->deltaBaixa(atual->baixaAtual, prox->baixaAtual);
                temp->indMovDir(temp->dAlta,temp->dBaixa);
                temp->faixaVerdade(atual->altaAtual, atual->baixaAtual, prox->fechAnt);

                // 'temp' recebe as vari�veis do dia anterior de 'atual'
                temp->altaAtual = atual->altaAtual;
                temp->baixaAtual = atual->baixaAtual;
                temp->fechAnt = atual->fechAnt;
                temp->data = atual->data;
            }

            //cria uma lista de dados ADX temporaria com todos os campos
            // essa lista sera utilizada para os calculos seguintes

            ADX *p = new ADX (temp->data, temp->dAlta,temp->dBaixa, temp->indMovDirPos,
                              temp->indMovDirNeg,temp->FV, temp->FVMed, temp->MDMpos,
                              temp->MDMneg, temp->DX, temp->resADX);
            beta.push_back(p);

            iter1++;
            iter2++;
        }

    }
    else
    {
        cerr << "lista vazia.";
        return dadosGer;

    }

    //percorre a lista na ordem inversa para calculos envolvendo medias iniciais
    // utilizando as seguintes variaves de suporte
    list<ADX*>::iterator iter3 = beta.end(); // interador para lista temporaria, item atual
    list<ADX*>::iterator iter4 = beta.end(); // interador para lista temporaria, item anterior
    iter3--; iter3--;// para iniciar na posicao correta na lista, ignorando a primeira linha
    iter4--; iter4--;
    double somaPos = 0.0; // soma dos N Dm+
    double somaNeg = 0.0; // soma dos N Dm-
    double somaFV = 0.0;  //soma dos N TR
    double soma4 = 0.0; //soma DX
    int m = 0;   //contador primario ( para primeiras medias)
    int z = 0;  // contador secundario (para ADX)
    ADX *temp2 = new ADX();
    ADX *prox2 = new ADX();
    alfa =  (2.0 /(periodo+1.0)); // variavel de ajuda para calculo (2 /(periodo+1))


    /// calcula media inicial de DM+,DM- , FVMed e resADX referente ao periodo solicitado
    /// depois aplica formula exponencial nos demais ate terminar a lista temporaria
    while ( iter3 != beta.begin() )
    {
        temp2 = *iter3; // itens atuais na lista temporaria beta
        prox2 = *iter4; // itens anteriores na lista temporaria beta

        somaPos += temp2->indMovDirPos; //cria os somatorios para calculo da media inicial
        somaNeg += temp2->indMovDirNeg;
        somaFV += temp2->FV;
        temp2->MDMpos = 0.0; // zera os campos nao utilizados
        temp2->MDMneg = 0.0;
        temp2->FVMed = 0.0;
        temp2->resADX = 0.0;
        m++;  // contador


        //calcula as medias iniciais
        if ( m == periodo)
        {
            double media1 = ( somaPos/periodo); //media inicial para MDMpos
            double media2 = ( somaNeg/periodo ); // media inicial para MDMneg
            double media3 = (somaFV/ periodo);

            //seta os campos com as medias/valores iniciais
            // o primeiro MDM eh feito com media simples
            temp2->MDMpos = media1;
            temp2->MDMneg = media2;
            temp2->FVMed = media3;
            temp2->indiceDirecional();
            temp2->movimentoDirecional(); //DX
            soma4 += temp2->DX;

            iter4 = iter3;
            iter3--;

            //os proximos valores do mesmo campos usa o metodo da classe ADX
            while ( temp2 != beta.front())
            {
                temp2 = *iter3;  //itens atuais
                prox2 = *iter4;  // intens anteriores

                temp2->indMovDirMedio( periodo, prox2->MDMpos, prox2->MDMneg, alfa);
                temp2->faixaVerdMedia(periodo, prox2->FVMed, alfa);
                temp2->indiceDirecional(); // calcula DI+ e DI-
                temp2->movimentoDirecional(); // calcula DX
                soma4 += temp2->DX;        //somatorio de DX

                iter3--;
                iter4--;
                z++; //contagem para comecar adx

                if (z == (periodo-1) )
                {
                    //ADX inicial---------------------------------
                    double media4 = ( soma4/periodo); //media inicial para ADX
                    temp2->resADX = media4; //media simples de DX para N periodo
                    //fim do calculo do adx inicial-------------------------------------
                }
                if (z > (periodo-1))
                    // demais ADX
                    temp2->indiceDirecionalMedio(periodo, prox2->resADX, alfa);


                //cria a lista de retorno do metodo 'calculaADX()'
                //com data, DI+, DI- e ADX calculados
                ADX *n = new ADX (temp2->IDpos, temp2->IDneg, temp2->resADX, temp2->data);
                dadosGer.push_front(n);
            }

            break;
        }
        iter3--; // sobe na lista
    }

    return (dadosGer);

}


/// grava a lista de ADX calculados como arquivo texto (tipo csv)
void ListaADX:: grava()
{
    //se existem dados ADX calculados
    if ( dadosGer.size() != 0)
    {
        // cria arquivo texto em modo de saida
        ofstream gravacao ("ADX.csv", ios::out);

        //se o arquivo nao conseguir ser criado e aberto para gravacao
        if (!gravacao)
        {
            cerr << "arquivo nao pode ser criado " << endl;
            exit(1);
        }

        //se estiver ok, os dados sao gravados no arquivo texto ADX.csv
        else
        {
            ADX *objeto7 = new ADX();
            list<ADX*>::iterator iter7 = dadosGer.begin();

            //cria um cabecalho para o arquivo
            gravacao << fixed << setprecision(2) << "data" <<';'<< "DI+" <<';'<< "DI-"
                    << ';' << "ADX" << endl;

            //cria o arquivo com base no retorno do metodo 'calculaADX()'
            while (iter7 != dadosGer.end())
            {
                objeto7 = *iter7;
                gravacao << fixed << setprecision(2) << objeto7->data <<';'<< objeto7->IDpos
                        <<';'<< objeto7->IDneg << ';' << objeto7->resADX << endl;
                iter7++;
            }
        }

        //se ocorreu algum erro durante a escrita dos dados no arquivo texto
        if ( gravacao.fail())
            cerr << "\n Erro durante a gravacao." << endl;

        else
            cerr << " \n Gravacao efetuada com sucesso" << endl;
    }

    else
    {
        cerr << "Lista vazia. Gravacao nao foi efetuada. " << endl;
    }
}
